﻿using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Input;
using Peter.Common;

namespace Peter.Sdw.Logic
{
   /// <summary>
   /// View model used to edit a list of entries.
   /// </summary>
   public class EditorEditEntriesModel : EditorAddModel
   {
      private bool m_NoConvert;
      private bool m_LineConvert;
      private bool m_SpaceConvert;
      private bool m_RegexConvert;
      private bool m_KeepEmptyLines;
      private string m_TextToFormat;
      private string m_ConvertRegex;
      private RelayCommand m_CmdAddRemove;
      private RemoveRegex m_SelectedRemove;
      private readonly ObservableCollection<RemoveRegex> m_Removes = new ObservableCollection<RemoveRegex> ();

      /// <summary>
      /// Gets or Sets no conversion
      /// </summary>
      public bool NoConvert
      {
         get { return this.m_NoConvert; }
         set
         {
            this.m_NoConvert = value;
            this.RaisePropertyChanged ("NoConvert");
            this.Format ();
         }
      }

      /// <summary>
      /// Gets or Sets line conversion
      /// </summary>
      public bool LineConvert
      {
         get { return this.m_LineConvert; }
         set
         {
            this.m_LineConvert = value;
            this.RaisePropertyChanged ("LineConvert");
            this.Format ();
         }
      }

      /// <summary>
      /// Gets or Sets space conversion
      /// </summary>
      public bool SpaceConvert
      {
         get { return this.m_SpaceConvert; }
         set
         {
            this.m_SpaceConvert = value;
            this.RaisePropertyChanged ("SpaceConvert");
            this.Format ();
         }
      }

      /// <summary>
      /// Gets or Sets regular expression conversion
      /// </summary>
      public bool RegexConvert
      {
         get { return this.m_RegexConvert; }
         set
         {
            this.m_RegexConvert = value;
            this.RaisePropertyChanged ("RegexConvert");
            this.Format ();
         }
      }

      /// <summary>
      /// Gets or Sets the regex to use for conversion.
      /// </summary>
      public string ConvertRegex
      {
         get { return this.m_ConvertRegex; }
         set
         {
            this.m_ConvertRegex = value;
            this.RaisePropertyChanged ("ConvertRegex");
            this.Format ();
         }
      }

      /// <summary>
      /// Gets the list of items to remove.
      /// </summary>
      public ObservableCollection<RemoveRegex> Removes { get { return this.m_Removes; } }

      /// <summary>
      /// Gets the command used to add a new remove item.
      /// </summary>
      public ICommand RemoveCommand
      {
         get { return this.m_CmdAddRemove ?? (this.m_CmdAddRemove = new RelayCommand (this.OnRemoveAction)); }
      }

      /// <summary>
      /// Gets the currently selected remove regex.
      /// </summary>
      public RemoveRegex SelectedRemove
      {
         get { return this.m_SelectedRemove; }
         set
         {
            this.m_SelectedRemove = value;
            this.RaisePropertyChanged("SelectedRemove");
         }
      }

      /// <summary>
      /// Gets or Sets the text to format.
      /// </summary>
      public string TextToFormat
      {
         get { return this.m_TextToFormat; }
         set
         {
            this.m_TextToFormat = value;
            this.RaisePropertyChanged ("TextToFormat");
            this.Format ();
         }
      }

      /// <summary>
      /// Gets or Sets if we should keep empty line when removing line breaks.
      /// </summary>
      public bool KeepEmptyLines
      {
         get { return this.m_KeepEmptyLines; }
         set
         {
            this.m_KeepEmptyLines = value;
            this.RaisePropertyChanged ("KeepEmptyLines");
            this.Format ();
         }
      }

      /// <summary>
      /// Executes the remove action command.
      /// </summary>
      /// <param name="obj">Command parameter.</param>
      private void OnRemoveAction (object obj)
      {
         var action = obj.ToString ().ToLower ();
         if (action == "add") {
            var rmv = new RemoveRegex ();
            rmv.PropertyChanged += this.OnRemoveChanged;
            this.Removes.Add (new RemoveRegex ());
         } else {
            this.SelectedRemove.PropertyChanged -= this.OnRemoveChanged;
            this.Removes.Remove (this.SelectedRemove);
         }
         this.Format ();
      }

      /// <summary>
      /// Occurs when a removed regex changes.
      /// </summary>
      /// <param name="sender">Remove regex.</param>
      /// <param name="e">PropertyChangedEventArgs</param>
      private void OnRemoveChanged (object sender, PropertyChangedEventArgs e)
      {
         this.Format();
      }

      /// <summary>
      /// Formats the text with the given options.
      /// </summary>
      private void Format ()
      {
         if (string.IsNullOrWhiteSpace (this.TextToFormat)) return;
         var order = 1;
         var formattedText = new StringBuilder ();
         var unformattedText = this.TextToFormat.Trim ();
         foreach (var remove in this.Removes) {
            unformattedText = remove.RemoveFrom (unformattedText);
         }
         if (this.LineConvert) {
            using (var reader = new StringReader (unformattedText)) {
               while (reader.Peek () != -1) {
                  var line = reader.ReadLine ();
                  if (!string.IsNullOrWhiteSpace (line)) {
                     formattedText.AppendLine (string.Format ("[o]{0}|[t]{1}", order, line));
                  }
                  order++;
               }
            }
         } else if (this.RegexConvert) {
            if (string.IsNullOrWhiteSpace (this.ConvertRegex)) return;
            var matches = Regex.Matches (unformattedText, this.ConvertRegex, RegexOptions.IgnoreCase);
            /*[n]Number|[o]Order|[t]Text|[h]Header|[f@index]footer|[f@index]footer|...*/
            foreach (Match match in matches) {
               var orderGroup = match.Groups ["o"];
               var numberGroup = match.Groups ["n"];
               formattedText.Append (string.Format ("[o]{0}{1}|[t]{2}",
                  orderGroup.Success ? Convert.ToInt32 (orderGroup.Value) : order,
                  numberGroup.Success ? "|[n]" + numberGroup.Value : string.Empty,
                  match.Groups ["t"].Value.Replace ("\"", "&quot;").Trim ()
               ));
               formattedText.AppendLine ();
               order++;
            }
         } else if (this.SpaceConvert) {
            formattedText.AppendFormat ("[o]{0}|[t]", order);
            using (var reader = new StringReader (unformattedText)) {
               while (reader.Peek () != -1) {
                  var line = reader.ReadLine ();
                  if (line != null) {
                     if (string.IsNullOrWhiteSpace (line) && this.KeepEmptyLines)
                        formattedText.AppendLine ();
                     else if (!string.IsNullOrWhiteSpace (line))
                        formattedText.Append (" " + line);
                  }
               }
            }
         } else {
            formattedText.AppendFormat ("[o]{0}|[t]{1}", order, unformattedText);
         }
         this.Editor.SetText(formattedText.ToString ().Trim ());
      }
   }
}
